﻿using System;
using System.IO;
using System.Runtime.Serialization;
using System.ServiceModel.Channels;

namespace RockBus.DataContracts
{
    [DataContract(Namespace = StringConstants.RockBusNamespace)]
    public class ProcessingData
    {
        private ProcessingData()
        {
        }

        public ProcessingData(Guid correlationId, Guid messageId, ExchangeTypeEnum publisherExchangeType, MessageVersion publisherMessageVersion, MessageDirectionEnum messageDirectionEnum = MessageDirectionEnum.Received)
            : this()
        {
            this.CorrelationId = correlationId;
            this.MessageId = messageId;
            this.PublisherExchangeType = publisherExchangeType;
            this.PublisherMessageVersion = publisherMessageVersion;
            this.MessageDirection = messageDirectionEnum;
        }

        public ProcessingData(ProcessingData other)
            : this(other.CorrelationId, other.MessageId, other.PublisherExchangeType, other.PublisherMessageVersion, other.MessageDirection)
        {
        }

        [DataMember]
        public Guid CorrelationId { get; protected set; }

        [DataMember]
        public Guid MessageId { get; protected set; }

        [DataMember]
        public ExchangeTypeEnum PublisherExchangeType { get; set; }

        [IgnoreDataMember]
        public MessageVersion PublisherMessageVersion { get; set; }

        [DataMember]
        public MessageDirectionEnum MessageDirection { get; set; }

        public bool IsPublisherRequestResponse
        {
            get
            {
                return this.PublisherExchangeType == ExchangeTypeEnum.OneWayRequestResponse ||
                        this.PublisherExchangeType == ExchangeTypeEnum.TwoWayAckRequestResponse ||
                        this.PublisherExchangeType == ExchangeTypeEnum.TwoWayRequestResponse;
            }
        }

        public bool IsRequest
        {
            get { return (this.MessageDirection == MessageDirectionEnum.InboundRequest) || (this.MessageDirection == MessageDirectionEnum.OutboundRequest); }
        }

        public bool IsResponse
        {
            get { return (this.MessageDirection == MessageDirectionEnum.InboundResponse) || (this.MessageDirection == MessageDirectionEnum.OutboundResponse); }
        }

        public bool IsInbound
        {
            get { return (this.MessageDirection == MessageDirectionEnum.InboundRequest) || (this.MessageDirection == MessageDirectionEnum.InboundResponse); }
        }

        public bool IsOutbound
        {
            get { return (this.MessageDirection == MessageDirectionEnum.OutboundRequest) || (this.MessageDirection == MessageDirectionEnum.OutboundResponse); }
        }

        public override string ToString()
        {
            return string.Format("CorrelationId: {0}; MessageId: {1}; MessageDirection: {2}; Version: {3}",
                this.CorrelationId,
                this.MessageId,
                this.MessageDirection,
                this.PublisherMessageVersion);
        }
    }

    [DataContract(Namespace = StringConstants.RockBusNamespace)]
    public class RoutingHeader
    {
        public const string HeaderName = "RoutingHeader";
        public const string DeliveryAttemptCountKey = "DeliveryAttemptCount";

        private static readonly DataContractSerializer _dcs = new DataContractSerializer(typeof(RoutingHeader));

        public static RoutingHeader CreateFromByteArray(byte[] routingHeaderByteArray)
        {
            using (MemoryStream ms = new MemoryStream(routingHeaderByteArray))
            {
                ms.Position = 0;
                return _dcs.ReadObject(ms) as RoutingHeader;
            }
        }

        private RoutingHeader()
        {
        }

        private RoutingHeader(ProcessingData processingData)
            : this()
        {
            this.ProcessingData = processingData;
            this.ProcessorInfo = new ProcessorInfo();
        }

        public RoutingHeader(ProcessingData processingData, DeliveryInfo deliveryInfo)
            : this(processingData)
        {
            this.DestinationEndpoint = deliveryInfo.EndpointInfo;
            this.SubscriptionDetail = deliveryInfo.SubscriptionDetail;
        }

        public RoutingHeader(ProcessingData processingData, SubscriptionDetail subscriptionDetail, EndpointInfo destinationEndpoint)
            : this(processingData)
        {
            this.SubscriptionDetail = subscriptionDetail;
            this.DestinationEndpoint = destinationEndpoint;
        }

        [DataMember]
        public ProcessingData ProcessingData { get; set; }

        [DataMember]
        public ProcessorInfo ProcessorInfo { get; private set; }

        [DataMember]
        public SubscriptionDetail SubscriptionDetail { get; set; }

        [DataMember]
        public EndpointInfo DestinationEndpoint { get; set; }

        public bool IsPersistent
        {
            get { return this.SubscriptionDetail == null || this.SubscriptionDetail.Persistent; }
        }

        public bool IsTransactional
        {
            get { return this.DestinationEndpoint.IsTransactional; }
        }

        public bool IsRequestResponse
        {
            get
            {
                return ((this.DestinationEndpoint.ExchangeType == ExchangeTypeEnum.OneWayRequestResponse) ||
                        (this.DestinationEndpoint.ExchangeType == ExchangeTypeEnum.TwoWayAckRequestResponse) ||
                        (this.DestinationEndpoint.ExchangeType == ExchangeTypeEnum.TwoWayRequestResponse));
            }
        }

        public byte[] GetByteArray()
        {
            using (MemoryStream ms = new MemoryStream())
            {
                _dcs.WriteObject(ms, this);
                return ms.ToArray();
            }
        }

        public override string ToString()
        {
            return string.Format("{0}; Destination: {1}; ProcessorInfo: {2}",
                base.ToString(),
                this.DestinationEndpoint,
                this.ProcessorInfo);
        }
    }
}